package com.zhupanlin.config;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zhupanlin.filter.MyUsernamePasswordAuthenticationFilter;
import com.zhupanlin.service.RememberService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.authentication.RememberMeServices;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.InMemoryTokenRepositoryImpl;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * 描述信息：
 *
 * @author zhupanlin
 * @version 1.0
 * @date 2024/12/20 10:02
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    @Override
    public UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager userDetailsManager = new InMemoryUserDetailsManager();
        userDetailsManager.createUser(User.withUsername("user").password("{noop}123").roles("admin").build());
        return userDetailsManager;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService());
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean
    public UsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter() throws Exception {
        UsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter = new MyUsernamePasswordAuthenticationFilter();
        usernamePasswordAuthenticationFilter.setFilterProcessesUrl("/doLogin");
        usernamePasswordAuthenticationFilter.setUsernameParameter("username");
        usernamePasswordAuthenticationFilter.setPasswordParameter("password");
        usernamePasswordAuthenticationFilter.setAuthenticationManager(authenticationManagerBean());
        // 设置认证成功时使用自定义的 rememberMeServices 将 cookie 写入客户端
        usernamePasswordAuthenticationFilter.setRememberMeServices(rememberMeServices());
        // 登录成功处理器
        usernamePasswordAuthenticationFilter.setAuthenticationSuccessHandler((request, response, authentication) -> {
            Map<String, Object> result = new HashMap<>();
            result.put("status", 200);
            result.put("message", "登录成功");
            result.put("data", authentication.getPrincipal());
            String json = new ObjectMapper().writeValueAsString(result);
            response.setContentType("application/json;charset=utf-8");
            response.setStatus(HttpStatus.OK.value());
            response.getWriter().write(json);
        });
        usernamePasswordAuthenticationFilter.setAuthenticationFailureHandler((request, response, ex) -> {
            Map<String, Object> result = new HashMap<>();
            result.put("status", 500);
            result.put("message", ex.getMessage());
            String json = new ObjectMapper().writeValueAsString(result);
            response.setContentType("application/json;charset=utf-8");
            response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
            response.getWriter().write(json);
        });

        return usernamePasswordAuthenticationFilter;
    }

    @Bean
    public RememberMeServices rememberMeServices() {
        return new RememberService(UUID.randomUUID().toString(), userDetailsService(), new InMemoryTokenRepositoryImpl());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .and()
                .rememberMe() // 开启记住我功能 cookie 实现 1.认证成功保存记住我 cookie 到客户端 2.只有 cookie 写入客户端成功才能实现自动登录功能
                .rememberMeServices(rememberMeServices()) // 设置自动登录使用哪个 rememberMeServices
                .and()
                .exceptionHandling()
                .authenticationEntryPoint((request, response, ex) -> {
                    response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
                    response.setStatus(HttpStatus.UNAUTHORIZED.value());
                    response.getWriter().println("请认证之后再处理");
                })
                .and()
                .logout()
                .logoutRequestMatcher(new OrRequestMatcher(new AntPathRequestMatcher("/logout", HttpMethod.DELETE.name()),
                                                           new AntPathRequestMatcher("/logout", HttpMethod.GET.name())))
                .logoutSuccessHandler((request, response, authentication) -> {
                    Map<String, Object> result = new HashMap<>();
                    result.put("status", 200);
                    result.put("message", "logout");
                    String json = new ObjectMapper().writeValueAsString(result);
                    response.setContentType("application/json;charset=utf-8");
                    response.getWriter().write(json);
                })
                .and()
                .csrf().disable();

        http.addFilterAt(usernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    }
}
